/*
Package limiter
@Author   : Uyynot
@Email    : uyynot@qq.com
@Time     : 2024/3/27 15:56
@File     : limiter.go
@Project  : gohub
@Desc     : 处理限流
*/
package limiter

import (
	"github.com/gin-gonic/gin"
	limiterlib "github.com/ulule/limiter/v3"
	sredis "github.com/ulule/limiter/v3/drivers/store/redis"
	"gohub/pkg/config"
	"gohub/pkg/logger"
	"gohub/pkg/redis"
	"strings"
)

// GetKeyIP
// @Description: 获取Limitor的key值，这里是IP地址（ip限流）
// @param c
// @return string
func GetKeyIP(c *gin.Context) string {
	return c.ClientIP()
}

// routeToKeyString
// @Description: 辅助方法，将URL中的/转为-；:转为_
// @param routeName
// @return string
func routeToKeyString(routeName string) string {
	return strings.ReplaceAll(strings.ReplaceAll(routeName, "/", "-"), ":", "_")
}

// GetKeyRouteWithIP
// @Description: Limitor 的 Key，路由+IP，针对单个路由做限流
// @param c
// @return string
func GetKeyRouteWithIP(c *gin.Context) string {
	return routeToKeyString(c.FullPath()) + c.ClientIP()
}

// CheckRate
// @Description: 检查请求是否超额
// @param c
// @param key
// @param formatted
// @return limiterlib.Context
// @return error
func CheckRate(c *gin.Context, key string, formatted string) (limiterlib.Context, error) {
	//实例化以来的limiter包的limiter.Rate对象
	var context limiterlib.Context
	rate, err := limiterlib.NewRateFromFormatted(formatted)
	if err != nil {
		logger.LogIf(err)
		return context, err
	}
	//初始化存储，使用我们程序里公用的redis.Redis对象
	store, err := sredis.NewStoreWithOptions(redis.Redis.Client, limiterlib.StoreOptions{
		//为limiter设置前缀，保持redis里数据整洁性
		Prefix: config.GetString("app.name") + ":limiter",
	})
	if err != nil {
		logger.LogIf(err)
		return context, err
	}
	//使用上面的初始化的limiter.Rate对象和存储对象
	limiterObj := limiterlib.New(store, rate)

	//获取限流的结果
	if c.GetBool("limiter-once") {
		//Peek()取结果，不增加访问次数
		return limiterObj.Peek(c, key)
	} else {
		//确保多个路由组里调用limitIP进行限流时，只增加一次访问次数
		c.Set("limiter-once", true)

		//Get()取结果且增加访问次数
		return limiterObj.Get(c, key)
	}
}
